#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
启动多个Handler服务进程

cronprocess --主进程
queueHandler --处理进程（子）

主进程每15分钟检查一次子进程状态，发现退出的子进程会自动启动
当主进程收到SIGINT信号时，向子进程发送SIGTERM信号

子进程主要负责队列数据转发处理
当子进程收到SIGTERM信号时，结束进程

Created on 2012-3-14
@author: zc
"""

import sys
import os
import getopt
import logging
import signal
import random
import redis
import subprocess
import gevent.socket as socket
import gevent
from gevent import monkey
from gevent.event import Event
from config.settings import  PROCESS_NUM, REDIS_SRV_HOST, REDIS_SRV_PORT, CHECK_SUBPROCESS_SECONDS, QUEUE_USER, QUEUE_TFEET
from queuehandler import queuetfeet, queueuser
monkey.patch_socket( dns = False, aggressive = True )
monkey.patch_os()
monkey.patch_time()


class cronprocess( object ):
    def __init__( self, cpus, sleepInterval ):
        self.ncpus = cpus #使用CPU数
        self.sleepInterval = sleepInterval
        self.stop_event = Event()
        self.logger = logger( os.getpid() )
        self.process = [subprocess.Popen( ( 'python2.7', sys.argv[0], 'queueHandler' ) )
                                for _ in xrange( self.ncpus )]
        gevent.sleep( self.sleepInterval )
        gevent.spawn( self.checkprocess )
        gevent.signal( signal.SIGINT, self.stop ) #拦截kill信号
        self.stop_event.wait()

    def checkprocess( self ):
        '''检查子进程状态 
        每self.sleepInterval秒检查一次所有子进程运行状态，若子进程退出则重新运行子进程
        self.sleepInterval = extauth.settings.CHECK_SUBPROCESS_SECONDS
        '''
        while True:
            gevent.sleep( self.sleepInterval )
            for p in self.process:
                if subprocess.Popen.poll( p ) is None:
                    self.logger.debug( '%d running.' % p.pid )
                    continue   
                else:
                    try:
                        self.process.remove( p )
                        new_process = subprocess.Popen( ( 'python2.7', sys.argv[0], 'queueHandler' ) )
                        self.process.append( new_process )
                        #print  '%d was dead. started a new process %d' % ( p.pid, new_process.pid ) 
                        self.logger.warn( '%d was dead. started a new process %d' % ( p.pid , new_process.pid ) )
                    except Exception, data:
                        self.logger.error( '{0}'.format( data ) )
            
    def stop( self ):
        #print 'i will kill myself....'
        for p in self.process:
            p.terminate()              #与os.kill( p.pid, signal.SIGTERM )功能一样，发送SIGTERM信号给子进程
            p.wait()
        self.stop_event.set()


class queueHandler( object ):
    """ 处理队列中需要转发的数据 """
    def __init__( self ):
        self.stop_event = Event()
        self.queue = redis.Redis( host = REDIS_SRV_HOST, port = REDIS_SRV_PORT )
        self.logger = None
        gevent.spawn( self.startHandler )
        gevent.sleep( 0 )
        #print 'queueObserver started:%d' % os.getpid()
        gevent.signal( signal.SIGTERM, self.stop ) #拦截主进程通知的SIGTERM信号
        self.stop_event.wait()
    
    def queueObserver( self, num = 0, sleepInterval = 0.2 ):
        ''' 队列处理，从redis 队列获取信息，转发到xmpp server '''
        self.logger = logger( os.getpid() )
        while True:
            try:
                tfeet = self.queue.lpop( QUEUE_TFEET )
                user = self.queue.lpop( QUEUE_USER )
                if tfeet is None and user is None:
                    gevent.sleep( sleepInterval )
                    continue
                if tfeet is not None:
                    gevent.spawn( queuetfeet, tfeet ) #spawn到协程处理
                if user is not None:
                    gevent.spawn( queueuser, user ) #spawn到协程处理
            except Exception , data:
                self.logger.error( 'ERROR>>>>>>:%s' % data )
    
    def startHandler( self ):
        ''' 启动队列数个协程，用于队列处理'''
        temp = 3
        threads = []
        for i in range( temp ):
            threads.append( gevent.spawn( self.queueObserver, i, 0.2 ) )
        gevent.joinall( threads )
    
    def stop( self ):
        print 'queueHandler(%s):will stop' % os.getpid()
        self.stop_event.set()


def logger( pid ):
    ''' logger ,每个进程log分别记录'''
    logfilename = '/var/log/weibo2tfeet_cronprocess_%d.log' % pid
    sys.stderr = open( logfilename , 'a' )
    logger = logging.getLogger()
    hdlr = logging.FileHandler( logfilename , mode = 'a' )
    formatter = logging.Formatter( "%(asctime)s %(levelname)s model:%(module)s pid:%(process)d %(message)s" )
    hdlr.setFormatter( formatter )
    logger.addHandler( hdlr )
    logger.setLevel( logging.ERROR )
    return logger


if __name__ == '__main__':
    if  len( sys.argv ) == 1:
        cronprocess( PROCESS_NUM, CHECK_SUBPROCESS_SECONDS )
    else:
        queueHandler()
